package net.wangds.yunz.auth.service;

import net.wangds.auth.resource.repo.rdbms.ResourceJpaRepo;
import net.wangds.auth.role.repo.rdbms.RoleJpaRepo;
import net.wangds.auth.role.repo.rdbms.RoleResRefJpaRepo;
import net.wangds.auth.token.cache.TokenCache;
import net.wangds.auth.user.entity.GeneralUserRoleRef;
import net.wangds.auth.user.repo.rdbms.UserResRefJpaRepo;
import net.wangds.auth.user.repo.rdbms.UserRoleRefJpaRepo;
import net.wangds.yunz.auth.comp.AuthAccountComp;
import net.wangds.yunz.auth.comp.VerifyCodeCache;
import net.wangds.data.DisableEnum;
import net.wangds.data.RemovedEnum;
import net.wangds.log.helper.LogHelper;
import net.wangds.program.auth.module.AuthAccount;
import net.wangds.utils.JsonUtils;
import net.wangds.validatecode.comp.ValidateCodeComp;
import net.wangds.validatecode.model.VerifyInfo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.function.Consumer;

@Service
@Profile({"program"})
public class AuthService {

    @Autowired
    private AuthAccountComp mmCmpM10Aa;

    @Autowired
    private UserRoleRefJpaRepo mmRepoUsrRole;


    @Autowired
    private UserResRefJpaRepo mmRepoUsrRes;

    @Autowired
    private RoleJpaRepo mmRepoRole;


    @Autowired
    private RoleResRefJpaRepo mmRepoRoleRes;

    @Autowired
    private ResourceJpaRepo mmRepoRes;

    @Autowired
    private TokenCache mmCacheToken;

    @Autowired
    private ValidateCodeComp mmCmpVerifyCode;

    @Autowired
    private VerifyCodeCache cacVerifyCode;


    public UserDetails getUserDetail(String token){
        return mmCacheToken.getToken(token);
    }


    /**
     * 发送短信验证码.
     * @param tel 电话号码.
     */
    public void sendSmsRegCode(String tel){

    }

    /**
     * 账户.
     * @param account 账户.
     * @return 账户.
     */
    public AuthAccount regNewAccount(AuthAccount account){
        return mmCmpM10Aa.save(AuthAccount.ofNewUser(account));
    }


    /**
     * 新普通账户.
     * @param loginName 登录名.
     * @param password 加密后的密码.
     * @param deviceId 设备id.
     * @param dispName 显示名称,真实姓名.
     * @param nickname 昵称.
     * @param desc 个人描述.
     * @param avatar 头型资源id.
     * @param referee 推荐人.
     * @return 新账户.
     */
    public AuthAccount regNewAccount(String loginName, String password, String deviceId,
                                     String dispName, String nickname, String desc,
                                     String avatar, String referee) {
        return mmCmpM10Aa.save(AuthAccount.ofNewUser(loginName,password,deviceId,
                dispName,nickname,desc,avatar,referee
                ));
    }


    /**
     * 发送登录验证码.
     * @param tel 电话.
     */
    public void sendSmsLoginCode(String tel){

    }


    public void auditAccountBusinessInfo(AuthAccount account){

    }

    /**
     * 加载用户的权限.
     * @param ud 用户信息.
     * @return 授权集合.
     */
    public Collection<GrantedAuthority> loadUserAuthentication(
            UserDetails ud) {
        List<GrantedAuthority> res = new ArrayList<>();
        if(ud==null
                ||!(ud instanceof AuthAccount)){
            return res;
        }

        AuthAccount acc = (AuthAccount)ud;

        if(StringUtils.equalsAnyIgnoreCase(acc.getLoginname(),"admin","manager1","wangds")){
            res.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
        }

        String usr_id = acc.getId();
        List<GeneralUserRoleRef> roles = mmRepoUsrRole.findByUserIdAndDisableAndRemoved(acc.getId(), DisableEnum.NORMAL, RemovedEnum.NORMAL);
        roles.forEach(ref->{
            mmRepoRole.findById(ref.getRoleId()).ifPresent(role->{
                res.add(new SimpleGrantedAuthority("ROLE_"+role.getRoleName()));

                mmRepoRoleRes.findByRoleId(role.getId()).forEach(r->{
                    mmRepoRes.findByIdAndRemoved(r.getResId(), RemovedEnum.NORMAL).ifPresent(rs->{
                        res.add(new SimpleGrantedAuthority("RES_"+r.getAction()+"_"+rs.toUrl().toString()));
                    });
                });

            });
        });

        //直接给用户的资源授权
        mmRepoUsrRes.findByUserId(usr_id).forEach(ref->{
            mmRepoRes.findByIdAndRemoved(ref.getResId(), RemovedEnum.NORMAL).ifPresent(rs->{
                res.add(new SimpleGrantedAuthority("RES_"+ref.getAction()+"_"+rs.toUrl().toString()));
            });
        });

        return res;
    }

    public boolean existsByLoginname(String loginname) {
        return this.mmCmpM10Aa.existsByLoginname(loginname);
    }

    public Optional<AuthAccount> findByLoginname(String loginname) {
        return this.mmCmpM10Aa.findNormalUserByLoginName(loginname);
    }

    /**
     * 获取一个新的验证码.
     * @return 验证码.
     */
    public VerifyInfo fetchVerifyCode(){
        return cacVerifyCode.fetchVerifyInfo(UUID.randomUUID().toString());
    }

    /**
     * 检验验证码.
     *
     * <p>验证通过后验证码也被清理.</p>
     *
     * @param token 令牌.
     * @param code 验证码.
     * @return 结果.
     */
    @SuppressWarnings("all")
    public boolean validateVerifyCode(String token, String code){
        VerifyInfo vi = cacVerifyCode.fetchVerifyInfo(token);
        boolean res = StringUtils.equalsIgnoreCase(code, vi.getCode());
        LogHelper.dev(()->"输入的验证码："+code+",系统验证码:"+(vi==null?"验证码丢失":vi.getCode())+",验证结果："+res);
        if(res){
            cacVerifyCode.evictVerifyInfo(token);
        }
        return res;
    }

    public Optional<AuthAccount> updateCurrentAccount(Consumer<AuthAccount> onUpdate){

        if(onUpdate==null){
            return Optional.empty();
        }

        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        Object ud = auth.getCredentials();

        if(ud instanceof AuthAccount){
            AuthAccount acc = (AuthAccount)ud;

            onUpdate.accept(acc);

            mmCmpM10Aa.findById(acc.getId()).ifPresent(old->{
                acc.setPassword(old.getPassword());
            });
            String sessionKey = acc.getSessionKey();
            AuthAccount res = mmCmpM10Aa.saveAndFlush(acc);
            res.setSessionKey(sessionKey);
            LogHelper.dev("更新当前账户信息:"+ JsonUtils.toJsonStr(res));

            mmCacheToken.putToken(acc.getSessionKey(),res);

            return Optional.of(res);
        }

        return Optional.empty();
    }

    public Page<AuthAccount> findFamous(String keywords, Pageable pg) {
        return mmCmpM10Aa.findFamous(keywords, PageRequest.of(pg.getPageNumber(), pg.getPageSize(),
                Sort.by(Sort.Order.desc("orderWeight"), Sort.Order.desc("createTm"))));
    }

    public Optional<AuthAccount> findById(String id) {
        return mmCmpM10Aa.findById(id);
    }
}
